from common.db import models
from apps.storage.models import PublicFile
from django.contrib.postgres.fields import JSONField
import time
from apps.repo.models import *
from apps.user.models import *
from apps.order.models import *
from django.utils.html import format_html
from django.db.models import Q
import uuid

# Create your models here.
# Other App


class Store(models.Model):
    """
    茶庄
    """

    class Meta:
        verbose_name = verbose_name_plural = '茶庄'

    class StoreType(models.Choices):
        BRAND = "品牌茶庄"
        GROWER = "茶农茶庄"

    class StoreTypeBits(models.Choices):
        BRAND = '1'
        GROWER = '0'


    name = models.CharField("茶庄名字")
    store_type = models.CharField("茶庄类型", choices=StoreType.get_choices())
    video_banner_public_file_id = models.CharField("视频Banner公开文件ID", max_length=64, blank=True, null=True, )

    def __str__(self):
        return self.name

    @property
    def banner_file_ids(self):
        return [e.banner_public_file_id for e in StoreBanner.objects.filter(store=self)]

    @property
    def video_banner_public_file(self) -> PublicFile:
        return PublicFile.objects.filter(id=self.video_banner_public_file_id).first()

    @property
    def video(self):
        if self.video_banner_public_file:
            if self.video_banner_public_file.url.split('.')[-1].lower() not in ['mp4', ]:
                return ''
            else:
                return self.video_banner_public_file.url
        else:
            return ''

    def get_banner_url(self, idx):
        b = StoreBanner.objects.filter(store=self, order=idx).first()
        return b.logo if b else ''

    def get_banner_id(self, idx):
        b = StoreBanner.objects.filter(store=self, order=idx).first()
        return b.id if b else ''

class StoreBanner(models.Model):
    """
    茶庄Banner
    """
    store = models.ForeignKey(Store, verbose_name="茶庄", on_delete=models.CASCADE)
    banner_public_file_id = models.CharField("Banner公开文件ID", max_length=64,
                                             )
    order = models.IntegerField("顺序")

    @property
    def logo_public_file(self) -> PublicFile:
        return PublicFile.objects.get(id=self.banner_public_file_id)

    @property
    def logo(self):
        return self.logo_public_file.url


def get_rand_3bit_no():
    return uuid.uuid4().hex[0:3].upper()


class Brand(models.Model):
    """
    品牌
    """

    class Meta:
        verbose_name = verbose_name_plural = '品牌'

    output_fields = ['id', 'name', 'logo', 'video', 'attention_account', 'store']

    store = models.ForeignKey(Store, verbose_name="茶庄", on_delete=models.CASCADE)
    name = models.CharField("品牌名")
    logo_public_file_id = models.CharField("BrandLogo公开文件ID", max_length=64, )
    attention_account = models.IntegerField("关注数")
    video_banner_public_file_id = models.CharField("视频Banner公开文件ID", max_length=64, blank=True, null=True,
                                                   )
    no = models.CharField("品牌编号（品牌首字母）", unique=True, default=get_rand_3bit_no)

    def __str__(self):
        return self.name

    @property
    def logo_public_file(self) -> PublicFile:
        return PublicFile.objects.get(id=self.logo_public_file_id)

    @property
    def logo(self):
        return self.logo_public_file.url if self.logo_public_file else ''

    @property
    def video_banner_public_file(self) -> PublicFile:
        return PublicFile.objects.filter(id=self.video_banner_public_file_id).first()

    @property
    def video(self):
        if self.video_banner_public_file:
            if self.video_banner_public_file.url.split('.')[-1].lower() not in ['mp4', ]:
                return ''
            else:
                return self.video_banner_public_file.url
        else:
            return ''

    @property
    def attention_count(self):
        return UserAttentionBrand.objects.filter(brand_id_id=self.id).count().__str__()

    def is_attention(self, user_id):
        if UserAttentionBrand.objects.filter(user_id=user_id, brand_id_id=self.id).count() > 0:
            return True
        return False

    def get_banner_url(self, idx):
        b = BrandLongImage.objects.filter(brand=self, order=idx).first()
        return b.logo if b else ''

    def get_banner_id(self, idx):
        b = BrandLongImage.objects.filter(brand=self, order=idx).first()
        return b.id if b else ''

class BrandLongImage(models.Model):
    """
    品牌介绍长图
    """
    brand = models.ForeignKey(Brand, verbose_name='品牌', on_delete=models.CASCADE)
    banner_public_file_id = models.CharField("长图公开文件ID", max_length=64, )
    order = models.IntegerField("顺序")

    @property
    def logo_public_file(self) -> PublicFile:
        return PublicFile.objects.get(id=self.banner_public_file_id)

    @property
    def logo(self):
        return self.logo_public_file.url


class Package(models.Model):
    """
    产品包装
    当有产品应用此包装时，product_ids添加对应产品的ID
    """

    class Meta:
        verbose_name = verbose_name_plural = '包装'

    name = models.CharField(verbose_name='包装名称', unique=True)
    price = models.DecimalField("包装价格", max_digits=10, decimal_places=2)
    product_ids = JSONField(verbose_name='可使用的产品列表', default=[])
    avatar_public_file_id = models.CharField("Avatar公开文件ID", max_length=64, blank=True, null=True)

    @property
    def avatar_public_file(self) -> PublicFile:
        return PublicFile.objects.get(id=self.avatar_public_file_id)

    @property
    def avatar(self):
        return self.avatar_public_file.url

    def __str__(self):
        return self.name


class Product(models.Model):
    """
    产品
    """

    class Meta:
        verbose_name = verbose_name_plural = '产品'
        unique_together = (('no', 'tea_type', 'age_code', 'tea_cake_type'),)

    class ProductStatus(models.Choices):
        REPO_IN_ING = "入库中"
        REPO_ENTERED = "已入库"
        PRE_SALE_ING = "预售中"
        PRE_SALE_END = "预售结束"
        PRE_SALE_FAILED = "预售失败"

    class TeaType(models.Choices):
        """RAW-0 FERMENTED-1 BLACK-2"""
        TT_RAW = "生茶"
        TT_FERMENTED = "熟茶"
        TT_BLACK = "红茶"
        TT_WHITE = "白茶"

    class TeaTypeBits(models.Choices):
        TT_RAW = '0'
        TT_FERMENTED = '1'
        TT_BLACK = '2'
        TT_WHITE = '3'

    class TeaCakeType(models.Choices):
        SMALL = "小罐"
        G200 = "200克"
        G375 = "357克"
        G500 = '500克'
        G50 = "50克"
        G250 = "250克"

    class TeaCakeTypeWeight(models.Choices):
        SMALL = 100
        G200 = 200
        G375 = 357
        G500 = 500
        G50 = 50
        G250 = 250

    class TeaCakeTypeBits(models.Choices):
        SMALL = '0'
        G200 = '1'
        G375 = '2'
        G500 = '3'
        G50 = '4'
        G250 = '5'

    brand = models.ForeignKey(Brand, verbose_name="品牌", on_delete=models.CASCADE)
    name = models.CharField("产品名")
    tea_type = models.CharField("产品类型", choices=TeaType.get_choices(), default=TeaType.TT_RAW.name)
    no = models.CharField("编号(首字母)", default=get_rand_3bit_no)
    logo_public_file_id = models.CharField("ProductLogo", max_length=64)
    # stock = models.IntegerField("库存")
    age_code = models.CharField("年份码", blank=True, null=True)
    tea_cake_type = models.CharField("茶饼类型", choices=TeaCakeType.get_choices(), default=TeaCakeType.SMALL.name)
    status = models.CharField("状态", choices=ProductStatus.get_choices())
    pre_sale_price = models.DecimalField("预售价格", max_digits=10, decimal_places=2)
    pre_sale_weixin_article_link = models.CharField("预售微信公众号文章链接", blank=True, null=True)
    max_price = models.DecimalField("最高价", max_digits=10, decimal_places=2)
    avg_price = models.DecimalField("历史平均价", max_digits=10, decimal_places=2)  # 依据GoodsBlock里的avg_price实时更新
    avg_price_custom = models.DecimalField('自定义历史平均价', max_digits=10, decimal_places=2)
    last_trade_price = models.DecimalField("最后一次成交价", max_digits=10, decimal_places=2)
    up_down_percent = models.DecimalField("涨跌幅", max_digits=5, decimal_places=2)
    pre_sale_start_time = models.DateTimeField("预售开始时间")
    pre_sale_end_time = models.DateTimeField("预售结束时间", blank=True, null=True)
    is_unfolded = models.BooleanField(verbose_name="是否展开", default=False)
    package_ids = JSONField(verbose_name='可用包装列表', default=[])
    # BannerVideo
    video_banner_public_file_id = models.CharField("视频Banner", max_length=64, blank=True, null=True, )
    # 文本字段
    specification = models.CharField("规格", max_length=256)
    kind = models.CharField("种类", blank=True, null=True)
    material = models.CharField("原料", blank=True, null=True)
    repo_id = models.CharField("仓库ID",
                               max_length=64, blank=True, null=True)
    age = models.CharField("年份", blank=True, null=True)
    publish_count = models.IntegerField("发行量")
    publish_unit = models.CharField("发行单位", blank=True, null=True)
    technology = models.CharField("工艺", blank=True, null=True)
    tea_mountain = models.CharField("茶山", blank=True, null=True)
    altitude = models.IntegerField("海拔（米）", blank=True, null=True)
    ancient_tea_tree_count = models.IntegerField("古树量(棵）", blank=True, null=True)
    year_avg_temperature = models.CharField("年平均气温", blank=True, null=True)
    year_avg_precipitation = models.CharField("年平均降水", blank=True, null=True)
    made_in = models.CharField("产地", blank=True, null=True)
    #
    today_price_data = JSONField("日价格曲线", default={"points": [], "year": 0, "month": 0, "day": 0})
    week_price_data = JSONField("周价格曲线", default={"points": [], "week": -1})
    month_price_data = JSONField("月价格曲线", default={"points": [], "year": 0, "month": 0})
    #
    free_repo_cost = models.BooleanField(verbose_name='免存仓费', default=False)

    init_store_unit_task_id = models.CharField(verbose_name='初始化存储任务ID', default='')
    in_repo_task_id = models.CharField(verbose_name='入库任务ID', default='')
    dispatch_task_id = models.CharField(verbose_name='分发任务ID', default='')


    @classmethod
    def get_ignore_fields(cls):
        return ['today_price_data', 'week_price_data', 'month_price_data']

    @property
    def dispatch_task(self):
        from celery.result import AsyncResult
        ar = AsyncResult(self.dispatch_task_id) if self.dispatch_task_id else None
        return ar

    @property
    def dispatch_status(self):
        if not self.free_repo_cost:
            return '仓储费设置不允许分发'
        status = self.dispatch_task.status if self.dispatch_task else ''
        if not status:
            if self.status in [Product.ProductStatus.PRE_SALE_END.name, Product.ProductStatus.PRE_SALE_FAILED.name, Product.ProductStatus.PRE_SALE_ING.name]:
                status = '%s' % getattr(Product.ProductStatus, self.status).value
            elif self.status in [Product.ProductStatus.REPO_IN_ING.name, Product.ProductStatus.REPO_ENTERED.name]:
                if self.init_store_unit_task and self.init_store_unit_task.status == 'SUCCESS':
                    status = format_html('<a target="_blank" href="/product/admin/%s/dispatch">分发</a>' % self.pk)
                else:
                    status = '请先入库'
        else:
            if status == 'FAILURE':
                if self.init_store_unit_task and self.init_store_unit_task.status == 'SUCCESS':
                    status = format_html('<a target="_blank" href="/product/admin/%s/dispatch">重试分发</a>' % self.pk)
                else:
                    status = '请先入库'
            elif status == 'PENDING':
                if self.status not in [Product.ProductStatus.REPO_IN_ING.name, Product.ProductStatus.REPO_ENTERED.name]:
                    status = '-'
        return status

    @property
    def dispatch_result(self):
        return self.dispatch_task.result if self.dispatch_task else ''

    def get_set_avg_price_link(self):
        return format_html('<a target="_blank" href="/product/admin/%s/set-avg-price">设置指导价</a>' % (self.pk, ))
    get_set_avg_price_link.allow_tags = True
    get_set_avg_price_link.short_description = '设置指导价'


    @property
    def init_store_unit_task(self):
        from celery.result import AsyncResult
        ar = AsyncResult(self.init_store_unit_task_id) if self.init_store_unit_task_id else None
        return ar

    def init_store_unit_status(self):
        status = self.init_store_unit_task.status if self.init_store_unit_task else ''
        if not status:
            if self.status != Product.ProductStatus.REPO_IN_ING.name:
                status = '已入库'
            else:
                status = format_html('<a href="/system/products/%s/init-store-unit" target="_blank">批量入库</a>' % self.id)
        else:
            if status == 'FAILURE':
                status = format_html('<a href="/system/products/%s/init-store-unit" target="_blank">批量入库</a>' % self.id)
            elif status == 'PENDING':
                if self.status not in [Product.ProductStatus.REPO_IN_ING.name, Product.ProductStatus.REPO_ENTERED.name]:
                    status = '已入库'
        return status
    init_store_unit_status.allowed_tag = True
    init_store_unit_status.short_description = '批量入库'

    @property
    def init_store_unit_result(self):
        return self.init_store_unit_task.result if self.init_store_unit_task else ''

    def __str__(self):
        return self.name

    @property
    def line_data_count(self):
        tcount = len(self.today_price_data.get('points', []))
        wcount = len(self.week_price_data.get('points', []))
        mcount = len(self.month_price_data.get('points', []))
        return '%d-%d-%d' % (tcount, wcount, mcount)

    @property
    def keeping_count(self):
        return GoodsBlock.objects.filter(~Q(owner_user_id=None), product=self).exclude(
            trade_type=GoodsBlock.TradeType.TAKE_OUT.name).count()

    @property
    def trading_count(self):
        return MarketOrder.objects.filter(product=self, as_gift=False,
                                          publish_type=MarketOrder.PublishType.MARKET.name).count()

    @property
    def picked_count(self):
        return GoodsBlock.objects.filter(product=self, trade_type=GoodsBlock.TradeType.TAKE_OUT.name)

    def get_banner_url(self, idx):
        b = ProductLongImage.objects.filter(product=self, order=idx).first()
        return b.banner_url if b else ''

    @property
    def stock(self):
        return GoodsBlock.objects.filter(product_id=self.pk, sold=False).count()

    def in_repo_count(self):
        return GoodsBlock.objects.filter(~Q(trade_type=GoodsBlock.TradeType.TAKE_OUT.name), product=self).count()

    def out_repo_count(self):
        return GoodsBlock.objects.filter(product=self, trade_type=GoodsBlock.TradeType.TAKE_OUT.name).count()

    def get_make_gb_unique_bar_codes_link(self):
        return format_html(
            '<a target="_blank" href="/system/products/%s/make-gb-unique-bar-codes">生成唯一码</a>' % (self.id,))
    get_make_gb_unique_bar_codes_link.allow_tags = True
    get_make_gb_unique_bar_codes_link.short_description = '生成码'

    def get_batch_in_repo_link(self):
        return format_html('<a target="_blank" href="/system/products/%s/batch-in-repo">批量入库</a>' % self.pk)
    get_batch_in_repo_link.allow_tags = True
    get_batch_in_repo_link.short_description = '批量入库'

    def get_dispatch_link(self):
        return self.dispatch_status
    get_dispatch_link.allow_tags = True
    get_dispatch_link.short_description = '分发'

    def commented_by(self, user_id):
        return True if ProductComment.objects.filter(product=self, user_id=user_id) else False

    @property
    def outputed_today_price_data(self):
        from apps.user.models import User
        tmp = copy.copy(self.today_price_data)
        tmp['points'] = User.process_points(tmp['points'], 6)
        return tmp

    @property
    def outputed_week_price_data(self):
        from apps.user.models import User
        tmp = copy.copy(self.week_price_data)
        tmp['points'] = User.process_points(tmp['points'], 7)
        return tmp

    @property
    def outputed_month_price_data(self):
        from apps.user.models import User
        tmp = copy.copy(self.month_price_data)
        tmp['points'] = User.process_points(tmp['points'], 4)
        return tmp

    @property
    def weight(self):
        return getattr(self.TeaCakeTypeWeight, self.tea_cake_type).value

    @property
    def logo_public_file(self) -> PublicFile:
        return PublicFile.objects.filter(id=self.logo_public_file_id).first()

    @property
    def logo(self):
        return self.logo_public_file.url if self.logo_public_file else ''

    @property
    def video_banner_public_file(self) -> PublicFile:
        return PublicFile.objects.filter(id=self.video_banner_public_file_id).first()

    @property
    def video(self):
        if self.video_banner_public_file:
            if self.video_banner_public_file.url.split('.')[-1].lower() not in ['mp4', ]:
                return ''
            else:
                return self.video_banner_public_file.url
        else:
            return ''

    @property
    def logo_public_file(self) -> PublicFile:
        return PublicFile.objects.filter(id=self.logo_public_file_id).first()

    def long_image(self):
        return self.logo_public_file.url if self.logo_public_file else ''

    def _get_product_type_bits(self):
        return getattr(self.TeaTypeBits, self.tea_type).value

    def _get_brand_type_bits(self):
        return getattr(Store.StoreTypeBits, self.brand.store.store_type).value

    def _get_tea_cake_type_bits(self):
        return getattr(self.TeaCakeTypeBits, self.tea_cake_type).value

    def get_unique_code(self):
        start_bits = 'T'
        brand_type_bits = self._get_brand_type_bits()
        brand_no_bits = self.brand.no
        product_type_bits = self._get_product_type_bits()
        product_no_bits = self.no
        age_code_bits = self.age_code
        tea_cake_type_bits = self._get_tea_cake_type_bits()
        return [start_bits, brand_type_bits, brand_no_bits, product_type_bits, product_no_bits,
                age_code_bits, tea_cake_type_bits]

    def product_detail(self):
        import datetime
        from common import time_rest, int_2_2d
        pre_sale_start_end = '%s-%s' % (
            self.pre_sale_start_time.strftime('%Y.%m.%d'), self.pre_sale_end_time.strftime('%m.%d'))
        pre_sale_rest_times = self.pre_sale_end_time - datetime.datetime.now()
        tr = time_rest(pre_sale_rest_times.total_seconds())
        return {
            "id": self.id,
            "logo": self.logo,
            "name": self.name,
            "brand": self.brand.name,
            "store": self.brand.store.name,
            "stock": self.stock.__str__(),
            "pre_sale_price": self.pre_sale_price.__str__(),
            "pre_sale_start_end": pre_sale_start_end.__str__(),
            "pre_sale_rest_times": "%s天%s时%s分" % (int_2_2d(tr.days), int_2_2d(tr.hours), int_2_2d(tr.minutes)),
            "status": self.status.__str__(),
            "status_map": dict(self.ProductStatus.get_choices()),
            "keeping_count": self.keeping_count.__str__(),
            "avg_price": self.avg_price_custom.__str__(),
            "up_down_percent": '%.2f%%' % (self.up_down_percent * 100),
            "today_price_data": self.today_price_data,
            "week_price_data": self.week_price_data,
            "month_price_data": self.month_price_data,
        }

    @property
    def attention_count(self):
        return UserAttentionProduct.objects.filter(product_id=self.id).count().__str__()

    def is_attention(self, user_id):
        if UserAttentionProduct.objects.filter(user_id=user_id, product_id=self.id).count() > 0:
            return True
        return False


class ProductAvgPrice(models.Model):
    product = models.ForeignKey(Product, verbose_name='产品', on_delete=models.CASCADE)
    avg_price = models.DecimalField("平均价", max_digits=10, decimal_places=2)

    @classmethod
    def get_line_data_for_product(cls, product, roll=True):
        day_parts = [0, 4, 8, 12, 16, 20]
        week_parts = [6,5,4,3,2,1,0]
        month_parts = [0, 7, 14, 21]
        import datetime, time
        now = datetime.datetime.now()
        year, month, day, hour = now.year, now.month, now.day, now.hour
        week = now.isoweekday()

        current_day_hour = 0
        for i in day_parts:
            if hour >= i:
                current_day_hour = i
            else:
                break
        today_last_point_time = datetime.datetime(year, month, day, current_day_hour, 0, 0)

        current_week_last_point_time = datetime.datetime(year, month, day, 0, 0, 0)

        current_month_last_point_time = datetime.datetime(year, month, day, 0, 0, 0) - datetime.timedelta(days=week-1)

        today_time_points = []
        week_time_points = []
        month_time_points = []

        for hp in day_parts[::-1]:
            tp = today_last_point_time - datetime.timedelta(hours=hp)
            if tp.day != now.day and not roll:
                continue
            today_time_points.append(tp)

        for i in week_parts:
            if i > week-1 and not roll:
                continue
            tp = current_week_last_point_time - datetime.timedelta(days=i)
            week_time_points.append(tp)

        for i in month_parts[::-1]:
            tp = current_month_last_point_time - datetime.timedelta(days=i)
            if tp.month != now.month and not roll:
                continue
            month_time_points.append(tp)

        today_data = {'points': []}
        week_data = {'points': []}
        month_data = {'points': []}

        for tp in today_time_points:
            ts = time.mktime(tp.timetuple())
            obj = cls.objects.filter(product=product, add_timestamp__lt=ts).order_by('-add_timestamp').first()
            if not obj:
                continue
            today_data['points'].append({'value': obj.avg_price.__str__(), 'time': tp.strftime('%H')})

        for tp in week_time_points:
            ts = time.mktime(tp.timetuple())
            obj = cls.objects.filter(product=product, add_timestamp__lt=ts).order_by('-add_timestamp').first()
            if not obj:
                continue
            week_data['points'].append({'value': obj.avg_price.__str__(), 'time': tp.isoweekday()})

        for tp in month_time_points:
            ts = time.mktime(tp.timetuple())
            obj = cls.objects.filter(product=product, add_timestamp__lt=ts).order_by('-add_timestamp').first()
            if not obj:
                continue
            month_data['points'].append({'value': obj.avg_price.__str__(), 'time': tp.strftime('%Y-%m-%d')})

        if len(today_data['points']) < 6:
            today_data['points'].insert(0, {'value': product.pre_sale_price.__str__(), 'time': 'PreSale'})
        if len(week_data['points']) < 7:
            week_data['points'].insert(0, {'value': product.pre_sale_price.__str__(), 'time': 'PreSale'})
        if len(month_data['points']) < 4:
            month_data['points'].insert(0, {'value': product.pre_sale_price.__str__(), 'time': 'PreSale'})

        return {
            'today_data': today_data,
            'week_data': week_data,
            'month_data': month_data,
        }


class ProductLinePointPrice(models.Model):
    class Kind(models.Choices):
        T = 'Today'
        W = 'Week'
        M = 'Month'
    product = models.ForeignKey(Product, verbose_name='产品', on_delete=models.CASCADE)
    datetime_point = models.DateTimeField(verbose_name='时间点')
    kind = models.CharField(verbose_name='种类')
    avg_price = models.DecimalField('平均价', max_digits=10, decimal_places=2)

    @classmethod
    def get_product_avg_price_by_sub_orders(cls, product, kind, roll=True):
        from apps.order.models import SubOrder, Order
        from pprint import pprint
        import datetime, time
        from django.db.models import Avg
        day_parts = [0, 4, 8, 12, 16, 20, 24]
        week_parts = [7, 6, 5, 4, 3, 2, 1, 0]
        month_parts = [0, 7, 14, 21, 28]
        now = datetime.datetime.now()
        year, month, day, hour = now.year, now.month, now.day, now.hour
        week = now.isoweekday()

        current_day_hour = 0
        for i in day_parts:
            if hour >= i:
                current_day_hour = i
            else:
                break
        today_last_point_time = datetime.datetime(year, month, day, current_day_hour, 0, 0)

        current_week_last_point_time = datetime.datetime(year, month, day, 0, 0, 0)

        current_month_last_point_time = datetime.datetime(year, month, day, 0, 0, 0) - datetime.timedelta(days=week-1)

        today_time_points = []
        week_time_points = []
        month_time_points = []

        for hp in day_parts[::-1]:
            tp = today_last_point_time - datetime.timedelta(hours=hp)
            today_time_points.append(tp)

        for i in week_parts:
            tp = current_week_last_point_time - datetime.timedelta(days=i)
            week_time_points.append(tp)

        for i in month_parts[::-1]:
            tp = current_month_last_point_time - datetime.timedelta(days=i)
            month_time_points.append(tp)

        today_time_ranges = [(today_time_points[i].timestamp(), today_time_points[i+1].timestamp()) for i in range(len(today_time_points)-1)]
        week_time_ranges = [(week_time_points[i].timestamp(), week_time_points[i+1].timestamp()) for i in range(len(week_time_points)-1)]
        month_time_ranges = [(month_time_points[i].timestamp(), month_time_points[i+1].timestamp()) for i in range(len(month_time_points)-1)]

        time_ranges = []
        if kind == cls.Kind.T.name:
            time_ranges = today_time_ranges
        elif kind == cls.Kind.W.name:
            time_ranges = week_time_ranges
        elif kind == cls.Kind.M.name:
            time_ranges = month_time_ranges

        avg_price = SubOrder.objects.filter(
            product_id=product.id,
            order__status__in=[Order.Status.PAYED.name, Order.Status.FINISHED.name],
            create_type=SubOrder.CreateType.MARKET_BUY_IN.name,
            amount__gt=Decimal('0.00'),
            add_timestamp__gte=time_ranges[-1][0],
            add_timestamp__lt=time_ranges[-1][1]).values('amount').aggregate(
                avg_price=Avg('amount'))['avg_price']
        if avg_price is None:
            avg_price = product.pre_sale_price
        return avg_price

    @classmethod
    def get_line_data_for_product(cls, product, roll=True):
        """
            class CreateType(models.Choices):
                PRE_SALE_BUY_IN = "预售买入"
                MARKET_BUY_IN = "茶市买入"
                # DIRECTIONAL_SELL_OUT = "定向卖出"
                # DIRECTIONAL_BUY_IN = "定向买入"
                # MARKET_AUTO_MATCH = "茶市自动匹配"
                TAKE_OUT = "提仓"
        :param product:
        :param roll:
        :return:
        """
        from apps.order.models import SubOrder, Order
        from pprint import pprint
        import datetime, time
        from django.db.models import Avg
        day_parts = [0, 4, 8, 12, 16, 20, 24]
        week_parts = [7, 6, 5, 4, 3, 2, 1, 0]
        month_parts = [0, 7, 14, 21, 28]
        now = datetime.datetime.now()
        year, month, day, hour = now.year, now.month, now.day, now.hour
        week = now.isoweekday()

        current_day_hour = 0
        for i in day_parts:
            if hour >= i:
                current_day_hour = i
            else:
                break
        today_last_point_time = datetime.datetime(year, month, day, current_day_hour, 0, 0)

        current_week_last_point_time = datetime.datetime(year, month, day, 0, 0, 0)

        current_month_last_point_time = datetime.datetime(year, month, day, 0, 0, 0) - datetime.timedelta(days=week-1)

        today_time_points = []
        week_time_points = []
        month_time_points = []

        for hp in day_parts[::-1]:
            tp = today_last_point_time - datetime.timedelta(hours=hp)
            if tp.day != now.day and not roll:
                continue
            today_time_points.append(tp)

        for i in week_parts:
            if i > week-1 and not roll:
                continue
            tp = current_week_last_point_time - datetime.timedelta(days=i)
            week_time_points.append(tp)

        for i in month_parts[::-1]:
            tp = current_month_last_point_time - datetime.timedelta(days=i)
            if tp.month != now.month and not roll:
                continue
            month_time_points.append(tp)

        today_time_ranges = [(today_time_points[i].timestamp(), today_time_points[i+1].timestamp()) for i in range(len(today_time_points)-1)]
        week_time_ranges = [(week_time_points[i].timestamp(), week_time_points[i+1].timestamp()) for i in range(len(week_time_points)-1)]
        month_time_ranges = [(month_time_points[i].timestamp(), month_time_points[i+1].timestamp()) for i in range(len(month_time_points)-1)]

        today_data = {'points': []}
        week_data = {'points': []}
        month_data = {'points': []}

        for kind, _ in cls.Kind.get_choices():
            time_ranges = []
            if kind == cls.Kind.T.name:
                if cls.objects.filter(product=product, datetime_point=today_last_point_time, kind=cls.Kind.T.name):
                    continue
                time_ranges = today_time_ranges
            elif kind == cls.Kind.W.name:
                if cls.objects.filter(product=product, datetime_point=current_week_last_point_time, kind=cls.Kind.W.name):
                    continue
                time_ranges = week_time_ranges
            elif kind == cls.Kind.M.name:
                if cls.objects.filter(product=product, datetime_point=current_month_last_point_time, kind=cls.Kind.M.name):
                    continue
                time_ranges = month_time_ranges

            for ttr in time_ranges:
                if cls.objects.filter(product=product, datetime_point=datetime.datetime.fromtimestamp(ttr[1]), kind=kind):
                    continue
                avg_price = SubOrder.objects.filter(
                    product_id=product.id,
                    order__status__in=[Order.Status.PAYED.name, Order.Status.FINISHED.name],
                    create_type=SubOrder.CreateType.MARKET_BUY_IN.name,
                    amount__gt=Decimal('0.00'),
                    add_timestamp__gte=ttr[0], add_timestamp__lt=ttr[1]).values('amount').aggregate(avg_price=Avg('amount'))['avg_price']
                if avg_price is None:
                    last_point = cls.objects.filter(product=product, kind=kind).order_by('-add_timestamp').first()
                    if last_point:
                        avg_price = last_point.avg_price
                    else:
                        avg_price = product.pre_sale_price
                cls.objects.create(product=product, datetime_point=datetime.datetime.fromtimestamp(ttr[1]), avg_price=avg_price, kind=kind)

        today_data['points'] = [{'datetime_point': obj.datetime_point.strftime('%Y-%m-%d %H:%M:%S %w'), 'value': obj.avg_price.__str__()} for obj in cls.objects.filter(product=product, kind='T').order_by('-add_timestamp')[0:6]]
        week_data['points'] = [{'datetime_point': obj.datetime_point.strftime('%Y-%m-%d %H:%M:%S %w'), 'value': obj.avg_price.__str__()} for obj in cls.objects.filter(product=product, kind='W').order_by('-add_timestamp')[0:7]]
        month_data['points'] = [{'datetime_point': obj.datetime_point.strftime('%Y-%m-%d %H:%M:%S %w'), 'value': obj.avg_price.__str__()} for obj in cls.objects.filter(product=product, kind='M').order_by('-add_timestamp')[0:4]]

        today_data['points'] = today_data['points'][::-1]
        week_data['points'] = week_data['points'][::-1]
        month_data['points'] = month_data['points'][::-1]

        return {
            'today_data': today_data,
            'week_data': week_data,
            'month_data': month_data,
        }

class ProductLongImage(models.Model):
    """
    产品介绍长图
    """
    product = models.ForeignKey(Product, verbose_name='产品', on_delete=models.CASCADE)
    banner_public_file_id = models.CharField("长图公开文件ID", max_length=64)
    order = models.IntegerField("顺序")

    @property
    def banner_public_file(self) -> PublicFile:
        return PublicFile.objects.get(id=self.banner_public_file_id)

    @property
    def banner_url(self):
        return self.banner_public_file.url


class ProductComment(models.Model):
    """
    产品评论
    """
    user_id = models.CharField("用户ID", max_length=64)
    product = models.ForeignKey(Product, verbose_name='产品', on_delete=models.CASCADE)
    content = models.TextField("内容")


class ProductCommentPictures(models.Model):
    """
    产品评论附图
    """
    product_comment = models.ForeignKey(ProductComment, verbose_name='产品评论', on_delete=models.CASCADE)
    picture_public_file_id = models.CharField("banner公开文件ID", max_length=64)
    order = models.IntegerField("顺序")

    @property
    def picture_public_file(self) -> PublicFile:
        return PublicFile.objects.get(id=self.picture_public_file_id)

    @property
    def picture(self):
        return self.picture_public_file.url


class UserAttentionBrand(models.Model):
    """
    用户关注品牌
    """
    user_id = models.CharField("用户ID", max_length=64)
    brand_id = models.ForeignKey(Brand, verbose_name="品牌", on_delete=models.CASCADE)

    @property
    def att_count(self):
        return UserAttentionBrand.objects.filter(brand_id=self.brand_id).count()


class UserAttentionProduct(models.Model):
    """
    用户关注产品
    """
    user_id = models.CharField("用户ID", max_length=64)
    product_id = models.ForeignKey(Product, verbose_name="产品", on_delete=models.CASCADE)

    @property
    def att_count(self):
        return UserAttentionProduct.objects.filter(product_id_id=self.product_id).count().__str__()


class UserCollectProduct(models.Model):
    """
    用户收藏产品
    """
    user_id = models.CharField("用户ID", max_length=64)
    product_id = models.ForeignKey(Product, verbose_name="产品", on_delete=models.CASCADE)


class MarketOrder(models.Model):
    """
    市场挂单
    """

    class Meta:
        ordering = ('-add_timestamp',)

    class IntentionType(models.Choices):
        BUY = "买入"
        SELL = "卖出"
        TAKE_OUT = "提仓"

    class PublishType(models.Choices):
        MARKET = "发现（产品互易）"
        DIRECTIONAL = "定向"
        TAKE_OUT = "提仓"

    class Status(models.Choices):
        ING = "赠送中"  # /进行中
        TRADE = "交易中"
        END = "结束"
        CANCELED = "取消"

    user_id = models.CharField("用户ID", max_length=64)
    product = models.ForeignKey(Product, verbose_name="产品", on_delete=models.CASCADE)
    intention_type = models.CharField("意向类型", choices=IntentionType.get_choices())
    offered_price = models.DecimalField("出价", max_digits=10, decimal_places=2)
    count = models.IntegerField("数量")
    publish_type = models.CharField("发布类型", choices=PublishType.get_choices())
    direct_to_user_mobile = models.CharField("指定用户手机号", blank=True, null=True)
    direct_to_user_id = models.CharField("指定用户ID", max_length=64, blank=True, null=True)
    status = models.CharField("状态", choices=Status.get_choices(), default=Status.ING.name)
    as_gift = models.BooleanField("赠送", default=False)
    as_gift_multiple = models.BooleanField('赠送多人', default=False)
    as_gift_append_memo = models.CharField('赠送留言', blank=True, null=True)
    buy_in_market_order = models.ForeignKey('self', verbose_name='关联买入挂单', blank=True, null=True,
                                            on_delete=models.CASCADE, related_name='bimo')
    manzu_ta = models.BooleanField(verbose_name='满足TA', default=False)
    matched_market_order = models.ForeignKey('self', verbose_name='被匹配单', blank=True, null=True,
                                             on_delete=models.CASCADE, related_name='mmo')

    def __str__(self):
        return '%s-%s-%s' % (self.pk, self.product.name, self.goods_blocks_unique_codes)

    class BusinessType(models.Choices):
        MARKET_TRADE_SELL = '市场交易-卖'
        MARKET_TRADE_BUY = '市场交易-求购'
        DIRECT_SELL = '指定交易-定向卖'
        AS_GIFT_FRIEND = '赠送朋友'
        AS_GIFT_MULTIPLE = '赠送多人'
        TAKE_OUT = '提仓'

    @property
    def business_type(self):
        if self.intention_type == self.__class__.IntentionType.SELL.name and self.publish_type == self.__class__.PublishType.MARKET.name:
            return self.__class__.BusinessType.MARKET_TRADE_SELL.name
        elif self.intention_type == self.__class__.IntentionType.BUY.name and self.publish_type == self.__class__.PublishType.MARKET.name:
            return self.__class__.BusinessType.MARKET_TRADE_BUY.name
        elif self.intention_type == self.__class__.IntentionType.SELL.name and self.publish_type == self.__class__.PublishType.DIRECTIONAL.name:
            return self.__class__.BusinessType.DIRECT_SELL.name
        elif self.as_gift and not self.as_gift_multiple:
            return self.__class__.BusinessType.AS_GIFT_FRIEND.name
        elif self.as_gift and self.as_gift_multiple:
            return self.__class__.BusinessType.AS_GIFT_MULTIPLE.name
        elif self.publish_type == self.__class__.PublishType.TAKE_OUT.name:
            return self.__class__.BusinessType.TAKE_OUT.name
        else:
            raise JsonAPIException('DATA_ERROR', msg='挂单数据异常', status=500)

    @property
    def gift_out_datetime(self):
        import time
        return time.strftime("%Y.%m.%d %H:%M", time.localtime(self.add_timestamp))

    @property
    def gift_accept_datetime(self):
        import time
        related_order = self.related_order
        if not related_order:
            return ''
        else:
            if related_order.status == Order.Status.FINISHED.name:
                return time.strftime("%Y.%m.%d %H:%M", time.localtime(related_order.update_timestamp))
            else:
                return ''

    @property
    def direct_to_user(self):
        return User.objects.filter(pk=self.direct_to_user_id).first()

    @property
    def goods_blocks(self):
        return GoodsBlock.objects.filter(market_order_id=self.pk)

    @property
    def goods_blocks_unique_codes(self):
        return [e.unique_code for e in self.goods_blocks]

    @property
    def user(self):
        from apps.user.models import User
        return User.objects.get(pk=self.user_id)

    @property
    def related_order(self):
        from apps.order.models import SubOrder, Order
        sub_orders = SubOrder.objects.filter(market_order_id=self.pk)
        if sub_orders:
            return sub_orders[0].order
        else:
            return None

    @property
    def related_order_status(self):
        if self.related_order:
            return self.related_order.status
        else:
            return None

    @classmethod
    def create_market_order(cls, user_id, product, offered_price, count, publish_type, intention_type, as_gift=False,
                            direct_to_user_mobile=None, direct_to_user_id=None, as_gift_append_memo=None,
                            buy_in_market_order=None):
        obj = cls(
            user_id=user_id,
            product=product,
            offered_price=offered_price,
            count=count,
            publish_type=publish_type,
            intention_type=intention_type,
            status=cls.Status.ING.name,
            buy_in_market_order=buy_in_market_order,
        )
        # direct_to_user_mobile direct_to_user_id as_gift as_gift_append_memo
        if as_gift:
            obj.as_gift = True
            obj.as_gift_append_memo = as_gift_append_memo
            obj.direct_to_user_id = direct_to_user_id
            obj.direct_to_user_mobile = direct_to_user_mobile
        else:
            obj.as_gift = False
            if publish_type == cls.PublishType.MARKET.name:
                pass
            elif publish_type == cls.PublishType.DIRECTIONAL.name:
                obj.direct_to_user_id = direct_to_user_id
                obj.direct_to_user_mobile = direct_to_user_mobile
        obj.save()
        return obj

    def is_attention(self, user_id):
        if user_id:
            if UserCollectMarketOrder.objects.filter(user_id=user_id, market_order_id=self.id).first():
                return True
        return False


class UserCollectMarketOrder(models.Model):
    """
    用户收藏挂单
    """
    user_id = models.CharField("用户ID", max_length=64)
    market_order = models.ForeignKey(MarketOrder, verbose_name="挂单", on_delete=models.CASCADE)


class GoodsBlock(models.Model):
    """
    区块
    """

    class Meta:
        ordering = ('add_timestamp',)
        verbose_name = verbose_name_plural = '产品区块'

    class TradeType(models.Choices):
        PRE_SALE = '预售'
        MARKET = "茶市"
        TAKE_OUT = "提仓"

    class UniqueCodeVersion(models.Choices):
        DOG_YEAR_SPRING = "狗年春天"

    class NoFieldsLabel(models.Choices):
        DOG_YEAR_SPRING = ['1', '2', '3']

    class NoFieldsRange(models.Choices):
        DOG_YEAR_SPRING = [range(ord('A'), ord('Z') + 1), range(ord('A'), ord('Z') + 1), range(0, 100)]

    class UniqueCodeFields(models.Choices):
        DOG_YEAR_SPRING = [
            {
                "id": "start_bits",
                "name": "囤囤",
                "digits_count": 1,
            },
            {
                "id": "brand_type_bits",
                "name": "品牌类型",
                "digits_count": 1,
            },
            {
                "id": "brand_no_bits",
                "name": "品牌编号",
                "digits_count": 3,
            },
            {
                "id": "product_type_bits",
                "name": "产品类型",
                "digits_count": 1,
            },
            {
                "id": "product_no_bits",
                "name": "产品编号",
                "digits_count": 3,
            },
            {
                "id": "age_code_bits",
                "name": "年份",
                "digits_count": 5,
            },
            {
                "id": "tea_cake_type_bits",
                "name": "品牌类型",
                "digits_count": 1,
            }
        ]

    output_fields = ['id', 'unique_code']
    secret_fields = ['password']

    product = models.ForeignKey(Product, verbose_name="产品", on_delete=models.CASCADE)
    unique_code = models.CharField("区块唯一码", unique=True)
    unique_code_version = models.CharField("唯一识别码版本",
                                           choices=UniqueCodeVersion.get_choices(),
                                           default=UniqueCodeVersion.DOG_YEAR_SPRING.name)
    repo_id = models.CharField("仓库ID", )
    store_unit_id = models.CharField("存储单元ID", blank=True, null=True, )
    owner_user_id = models.CharField("拥有者用户ID", blank=True, null=True)
    trade_type = models.CharField("交易方式(最近一次交易)", choices=TradeType.get_choices())
    market_order = models.ForeignKey(MarketOrder, verbose_name="隶属挂单", blank=True, null=True)
    no = models.CharField("茶饼号", blank=True, null=True)
    fields = JSONField("唯一码编码字段列表（不包括茶饼号）", default={})
    locked_by_user_id = models.CharField("锁定用户ID", blank=True, null=True)
    locked_expire_at = models.FloatField("开始锁定后将失效的时间戳，超时释放", default=0)
    trade_price = models.DecimalField("交易价", max_digits=10, decimal_places=2)
    avg_price = models.DecimalField("平均价", max_digits=10, decimal_places=2)  # 有交易时实时更新
    trade_datetime = models.DateTimeField("交易时间", blank=True,
                                          null=True)  # 用来计算仓储费的开始时间，每一次交易，此字段更新为SubOrder的end_datetime
    sold = models.BooleanField(verbose_name='是否已预售卖出', default=False)
    taken_out = models.BooleanField(verbose_name='是否已提仓', default=False)


    @property
    def readable_unique_code(self):
        return '%s%s' % (self.product.name, self.no)

    @property
    def repo_cost_price_per_100g_1d(self):
        if self.product.free_repo_cost:
            return Decimal('0.00')
        else:
            return self.repo.price_per_100g_1d

    @property
    def repo_days(self):
        import datetime
        now = datetime.datetime.now()
        start = self.trade_datetime
        cha_time = now - start
        days = cha_time.days
        return days

    @property
    def repo_amount(self):
        repo_amount = Decimal(str(self.repo_days)) * self.repo_cost_price_per_100g_1d * Decimal(str(self.product.weight))
        return round(repo_amount, 2)

    @classmethod
    def parse_unique_code(cls, unique_code):
        import traceback
        try:
            current_version = cls.UniqueCodeVersion.DOG_YEAR_SPRING.name
            fields = getattr(cls.UniqueCodeFields, current_version).value
            tt_bits = unique_code[0]
            brand_type_bits = unique_code[1]
            brand_no_bits = unique_code[2:5]
            product_type_bits = unique_code[5]
            product_no_bits = unique_code[6:9]
            age_code_bits = unique_code[9:14]
            tea_cake_type_bits = unique_code[14]
            tea_no = unique_code[15:]
            #
            #
            # print(tt_bits, brand_type_bits, brand_no_bits, product_type_bits, product_no_bits, age_code_bits,
            #       tea_cake_type_bits, tea_no)
            product = Product.objects.filter(no=product_no_bits,
                                             tea_type=Product.TeaTypeBits.get_key_name_by_value(product_type_bits),
                                             tea_cake_type=Product.TeaCakeTypeBits.get_key_name_by_value(
                                                 tea_cake_type_bits),
                                             age_code=age_code_bits
                                             ).first()
            product_fields = [tt_bits, brand_type_bits, brand_no_bits, product_type_bits, product_no_bits,
                              age_code_bits, tea_cake_type_bits]
            if not product:
                print('产品不存在')
                return None
            if product.get_unique_code() != product_fields:
                print('产品编码不匹配')
                return None
            if product.brand.no != brand_no_bits:
                print('品牌编码不匹配')
                return None
            return {
                "product": product,
                "unique_code": unique_code,
                "unique_code_version": current_version,
                "no": tea_no,
                "fields": product_fields,
                'trade_type': cls.TradeType.PRE_SALE.name,
            }
        except:
            traceback.format_exc()
            return None

    def is_out_repo(self):
        from apps.repo.models import StoreUnitThree
        su = StoreUnitThree.objects.get(pk=self.store_unit_id)
        if su.in_use:
            return False
        else:
            return True

    def is_out_repo_display(self):
        if self.is_out_repo():
            return '已出库'
        else:
            return '在库'

    @property
    def locked_by_user(self):
        return User.objects.filter(pk=self.locked_by_user_id).first()

    @property
    def locked_expire_at_dt_format(self):
        import time
        return time.strftime(self.datetime_format,
                             time.localtime(self.locked_expire_at)) if self.locked_expire_at else ''

    @property
    def repo(self):
        from apps.repo.models import Repo
        return Repo.objects.filter(id=self.repo_id).first()

    @property
    def market(self):
        return MarketOrder.objects.filter(id=self.market_order).first()

    @property
    def store_unit(self):
        from apps.repo.models import StoreUnitThree
        return StoreUnitThree.objects.get(pk=self.store_unit_id)

    @property
    def store_unit_repo_no(self):
        return self.store_unit.repo_no

    @property
    def owner_user(self):
        return User.objects.filter(pk=self.owner_user_id).first()

    def get_avg_price(self):
        from django.db.models import Avg
        from apps.order.models import SubOrder
        avg_price = SubOrder.objects.filter(goods_block_id=self.id).exclude(amount=Decimal("0.0")).exclude(
            create_type=SubOrder.CreateType.TAKE_OUT.name).aggregate(Avg('amount'))['amount__avg']
        if avg_price is None:
            return Decimal("0.00")
        else:
            return Decimal(str(avg_price))

    def make_product_avg_price(self):
        from django.db.models import Avg
        avg_price = self.__class__.objects.filter(product_id=self.product.id).exclude(
            trade_type=self.__class__.TradeType.TAKE_OUT).aggregate(Avg("avg_price"))["avg_price__avg"]
        if avg_price is None:
            return Decimal("0.00")
        else:
            return Decimal(str(avg_price))

    def get_no_int_fields(self):
        no = copy.copy(self.no)
        return [ord(no[0]), ord(no[1]), int(no[2:4])]

    @classmethod
    def get_next_no_int_fields(cls, no_int_fields, version="DOG_YEAR_SPRING"):
        if no_int_fields is None:
            return [ord('A'), ord('A'), 0]
        next_no_int_fields = copy.copy(no_int_fields, )
        my_ranges = getattr(GoodsBlock.NoFieldsRange, version).value
        for i in range(len(no_int_fields) - 1, -1, -1):
            my_range = my_ranges[i]
            if no_int_fields[i] < my_range[-1]:
                next_no_int_fields[i] += 1
                if i < len(no_int_fields) - 1:  # 修改后几位为第一个值
                    for k in range(i + 1, len(no_int_fields)):
                        next_no_int_fields[k] = my_ranges[k][0]
                break
        # print(int_fields, next_int_fields)
        if next_no_int_fields == no_int_fields:
            return None
        else:
            return next_no_int_fields

    @property
    def order_exist(self):
        suborder = SubOrder.objects.filter(goods_block_id=self.id).first()
        if suborder:
            if suborder.create_type == SubOrder.CreateType.PRE_SALE_BUY_IN.name and \
                    suborder.order.status == Order.Status.CREATED.name and \
                    suborder.buyer_user_id == self.locked_by_user_id:
                return True
            else:
                return False
        else:
            return False

    def get_block_detail(self):
        import datetime
        # 仓储天数
        now = datetime.datetime.now()
        start = self.trade_datetime
        cha_time = now - start
        days = cha_time.days + 1
        # 仓储费（天 * 仓储费 * 重量）
        repo_amount = self.repo_amount

        # 挂单状态
        market_status = 'NORMAL'
        if self.market_order:
            if self.market_order.status == MarketOrder.Status.ING.name:
                if self.market_order.intention_type == MarketOrder.IntentionType.SELL.name:
                    if self.market_order.publish_type == MarketOrder.PublishType.MARKET.name:
                        market_status = 'SELL_MARKET'
                    if self.market_order.publish_type == MarketOrder.PublishType.DIRECTIONAL.name:
                        market_status = 'SELL_DIRECTIONAL'
                if self.market_order.as_gift_multiple:
                    market_status = 'GIFT_MULTIPLE'

        if self.trade_type == self.TradeType.TAKE_OUT.name:
            sub_order = SubOrder.objects.filter(goods_block_id=self.id,
                                                create_type=SubOrder.CreateType.TAKE_OUT.name,
                                                order__status=Order.Status.PAYED.name)
            if sub_order.count() > 0:
                market_status = 'TAKE_OUT_ING'

        return {
            "id": self.id,
            "unique_code": self.unique_code,
            "repo_id": self.repo_id,
            "repo_name": self.repo.name,
            "repo_price_per_100g_1d": self.repo.price_per_100g_1d,
            "days": days,
            "repo_amount": repo_amount,
            "market_status": market_status,
            "product_name": self.product.name,
            'readable_unique_code': self.readable_unique_code,
        }

    def procedure_amount(self):
        return Decimal(0)

    def get_block_order(self):
        return {
            "id": self.id,
            "unique_code": self.unique_code,
            'readable_unique_code': self.readable_unique_code,
            "repo_id": self.repo_id,
            "repo_name": self.repo.name,
            "repo_price_per_100g_1d": self.repo_cost_price_per_100g_1d,
            "product_name": self.product.name
        }

    def repo_list(self):
        from apps.repo.models import Repo
        resp = []
        repo = Repo.objects.filter()
        if repo.count() > 0:
            for re in repo:
                if self.product.free_repo_cost:
                    price = Decimal('0.00')
                else:
                    price = re.price_per_100g_1d
                resp.append({
                    "repo_id": re.id,
                    "repo_name": re.name,
                    "price_per_100g_1d": price
                })
        return resp


class GoodsBlockChain(models.Model):
    """
    区块链
    """

    class ChainType(models.Choices):
        IN_REPO = "入库"
        PRE_SALE = "预售"
        PRE_SALE_BUY_IN = "预售买入"
        PUBLISH_SELL = "发布卖出"
        MARKET = "茶市买入"
        DIRECT_SELL = "指定卖出"
        DIRECT_GIVE = "指定送出"
        ACCEPT = "赠送接受"

    goods_block = models.ForeignKey(GoodsBlock, verbose_name="区块", on_delete=models.CASCADE)
    operator_user_id = models.CharField("操作者用户ID", blank=True, null=True)
    chain_type = models.CharField("链类型", choices=ChainType.get_choices())
    node_datetime = models.DateTimeField("节点日期时间")
    title = models.CharField("标题")
    contents = JSONField("内容", default=[])
    repo_info = models.CharField("仓储信息", blank=True, null=True)
    # 变量
    storage_repo_days = models.IntegerField("存仓天数", default=0)
    storage_repo_id = models.CharField("存仓ID", blank=True, null=True)
    trade_price = models.DecimalField("交易价", max_digits=10, decimal_places=2, default=Decimal('0.00'))

    @property
    def user(self):
        from apps.user.models import User
        return User.objects.get(pk=self.operator_user_id)

    @property
    def repo(self):
        from apps.repo.models import Repo
        return Repo.objects.get(pk=self.storage_repo_id)

    def get_block_chain_node_outputs(self):
        title = ""
        repo_info = ""
        contents = []
        CT = GoodsBlockChain.ChainType
        if self.chain_type == CT.IN_REPO.name:
            title = '产品入库'
            contents = ['录入产品唯一码', ]
        elif self.chain_type == CT.PRE_SALE.name:
            title = '在囤囤上架预售'
            contents = [
                '预售数量：%d' % self.goods_block.product.publish_count,
                '预售时间：%s-%s' % (
                    self.goods_block.product.pre_sale_start_time.strftime('%Y.%m.%d'),
                    self.goods_block.product.pre_sale_end_time.strftime('%m.%d')),
            ]
        elif self.chain_type == CT.PRE_SALE_BUY_IN.name:
            title = '用户 %s 买入' % self.user.nickname
            contents = [
                "产品唯一码：%s" % self.goods_block.unique_code,
                '成交价格：￥%s' % self.trade_price.__str__()
            ]

        elif self.chain_type == CT.PUBLISH_SELL.name:
            title = '发布卖出信息'
            repo_info = '存仓%d天 | 茶仓位置：%s' % (self.storage_repo_days, self.repo.name)
        elif self.chain_type == CT.DIRECT_SELL.name:
            title = '出售给指定用户'
            repo_info = '存仓%d天 | 茶仓位置：%s' % (self.storage_repo_days, self.repo.name)
        elif self.chain_type == CT.MARKET.name:
            title = '用户 %s 买入' % self.user.nickname
            contents = [
                '预售数量：%d' % self.goods_block.product.publish_count,
                '预售价格：￥%s' % self.trade_price.__str__(),
                '预售时间：%s-%s' % (
                    self.goods_block.product.pre_sale_start_time.strftime('%Y.%m.%d'),
                    self.goods_block.product.pre_sale_end_time.strftime('%m.%d')),
            ]
        elif self.chain_type == CT.DIRECT_GIVE.name:
            title = '赠送朋友'
            repo_info = '存仓%d天 | 茶仓位置：%s' % (self.storage_repo_days, self.repo.name)
        elif self.chain_type == CT.ACCEPT.name:
            title = '用户 %s 已接收' % self.user.nickname

        return {
            "title": title,
            "contents": contents,
            "repo_info": repo_info,
            "node_datetime": self.node_datetime.strftime('%Y.%m.%d %H:%M'),
            "chain_type": self.chain_type,
            "chain_type_map": dict(self.ChainType.get_choices())
        }

    @classmethod
    def create_chain(cls, chain_type, goods_block=None, order=None, sub_order=None, save=True, repo_id=None, trade_price=None):
        import datetime
        gbid = ''
        if isinstance(goods_block, GoodsBlock):
            gbid = goods_block.id
            repo_id = goods_block.repo_id
            trade_price = goods_block.trade_price
        else:
            gbid = goods_block
            repo_id = repo_id
            trade_price = trade_price
        obj = cls(
            goods_block_id=gbid,
            chain_type=chain_type,
            node_datetime=datetime.datetime.now(),
            storage_repo_id=repo_id,
            trade_price=trade_price
        )
        if chain_type == cls.ChainType.IN_REPO.name:
            pass
        if chain_type == cls.ChainType.PRE_SALE.name:
            pass
        elif chain_type == cls.ChainType.PRE_SALE_BUY_IN.name:
            obj.operator_user_id = order.buyer_user_id
        elif chain_type == cls.ChainType.PUBLISH_SELL.name:
            td = sub_order.repo_cost_end_datetime - sub_order.repo_cost_end_datetime
            storage_repo_days = td.days
            obj.operator_user_id = sub_order.seller_user_id
            obj.storage_repo_days = storage_repo_days
        elif chain_type == cls.ChainType.MARKET.name:
            obj.operator_user_id = order.buyer_user_id
        elif chain_type == cls.ChainType.DIRECT_SELL.name:
            td = sub_order.repo_cost_end_datetime - sub_order.repo_cost_end_datetime
            storage_repo_days = td.days
            obj.operator_user_id = sub_order.seller_user_id
            obj.storage_repo_days = storage_repo_days
        elif chain_type == cls.ChainType.DIRECT_GIVE.name:
            td = sub_order.repo_cost_end_datetime - sub_order.repo_cost_end_datetime
            storage_repo_days = td.days
            obj.operator_user_id = sub_order.seller_user_id
            obj.storage_repo_days = storage_repo_days
        elif chain_type == cls.ChainType.ACCEPT.name:
            obj.operator_user_id = order.buyer_user_id

        op = obj.get_block_chain_node_outputs()
        obj.title = op['title']
        obj.contents = op['contents']
        obj.repo_info = op['repo_info']

        if save:
            obj.save()

        return obj


class IndexBanner(models.Model):
    """
    首页轮播图
    """

    class Meta:
        verbose_name = verbose_name_plural = '首页轮播图'

    banner_public_file_id = models.CharField("banner公开文件ID", max_length=64)
    link = models.CharField("链接")

    def __str__(self):
        return self.link

    @property
    def banner(self) -> PublicFile:
        """Banner"""
        return PublicFile.objects.get(id=self.banner_public_file_id)

    @property
    def image_url(self):
        """Image Url"""
        return self.banner.url
